package com.ttg.common.utils;

import com.ttg.common.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @ClassName BigDecimalUtil
 * @Description BigDecimal工具类
 * @Author chenyou
 * @Date 2023/3/8 19:36
 * @Version 1.0
 */
@Slf4j
public class BigDecimalUtils {
    /** 默认运算精度 */
    private static final int DEF_DIV_SCALE = 4;
    private static final String EXCEPTION_MSG = "The scale must be a positive integer or zero";

    private BigDecimalUtils() {

    }

    /**
     * 提供精确的加法运算。入参类型：Integer,Long,Double,BigDecimal
     *
     * @param o1:
     * @param o2:
     * @return BigDecimal
     * @Author chenyou
     * @Date 2023/4/21 18:00
     */
    public static BigDecimal add(Object o1,Object o2)
    {
        BigDecimal b1 = convert(o1);
        BigDecimal b2 = convert(o2);
        return b1.add(b2);
    }

    /**
     * 提供精确的加法运算。
     * @param b1 被加数
     * @param b2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(Double b1, Double b2)
    {
       BigDecimal v1 = defaultZero(b1);
       BigDecimal v2 = defaultZero(b2);
        return v1.add(v2);
    }

    /**
     * 提供精确的加法运算。
     * @param b1 被加数
     * @param b2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(BigDecimal b1, BigDecimal b2)
    {
        b1 = defaultZero(b1);
        b2 = defaultZero(b2);
        return b1.add(b2);
    }

    /**
     * 提供精确的减法运算。入参类型：Integer,Long,Double,BigDecimal
     *
     * @param o1:
     * @param o2:
     * @return BigDecimal
     * @Author chenyou
     * @Date 2023/4/21 18:23
     */
    public static BigDecimal sub(Object o1,Object o2)
    {
        BigDecimal b1 = convert(o1);
        BigDecimal b2 = convert(o2);
        return b1.subtract(b2);
    }
    /**
     * 提供精确的减法运算。
     * @param b1 被减数
     * @param b2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(Double b1, Double b2)
    {
        BigDecimal v1 = defaultZero(b1);
        BigDecimal v2 = defaultZero(b2);
        return v1.subtract(v2);
    }

    /**
     * 提供精确的减法运算。
     * @param b1 被减数
     * @param b2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(BigDecimal b1, BigDecimal b2)
    {
        b1 = defaultZero(b1);
        b2 = defaultZero(b2);
        return b1.subtract(b2);
    }

    /**
     * 提供精确的乘法运算,入参类型：Integer,Long,Double,BigDecimal
     *
     * @param o1:
     * @param o2:
     * @return BigDecimal
     * @Author chenyou
     * @Date 2023/4/22 14:53
     */
    public static BigDecimal mul(Object o1, Object o2)
    {
        BigDecimal b1 = convert(o1);
        BigDecimal b2 = convert(o2);
        return mul(b1,b2,DEF_DIV_SCALE);
    }

    /**
     * 提供精确的乘法运算
     *
     * @param o1:
     * @param o2:
     * @param scale:
     * @return BigDecimal
     * @Author chenyou
     * @Date 2023-06-09 16:53
     */
    public static BigDecimal mul(Object o1, Object o2,int scale){
        if (scale < 0)
        {
            throw new IllegalArgumentException(EXCEPTION_MSG);
        }
        BigDecimal b1 = convert(o1);
        BigDecimal b2 = convert(o2);
        if (b1.compareTo(BigDecimal.ZERO) == 0)
        {
            return BigDecimal.ZERO;
        }
        return b1.multiply(b2).setScale(scale, RoundingMode.HALF_UP);
    }
    /**
     * 提供精确的乘法运算。
     * @param b1 被乘数
     * @param b2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(Double b1, Double b2)
    {
        BigDecimal v1 = defaultZero(b1);
        BigDecimal v2 = defaultZero(b2);
        return mul(v1,v2);
    }

    /**
     * 提供精确的乘法运算。
     * @param b1 被乘数
     * @param b2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(BigDecimal b1, BigDecimal b2)
    {
        b1 = defaultZero(b1);
        b2 = defaultZero(b2);
        return mul(b1,b2,DEF_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的乘法运算。由scale参数指定精度，以后的数字四舍五入。
     * @param b1 被乘数
     * @param b2 乘数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的积
     */
    public static BigDecimal mul(BigDecimal b1, BigDecimal b2, int scale)
    {
        if (scale < 0)
        {
            throw new IllegalArgumentException(EXCEPTION_MSG);
        }
        b1 = defaultZero(b1);
        b2 = defaultZero(b2);
        if (b1.compareTo(BigDecimal.ZERO) == 0)
        {
            return BigDecimal.ZERO;
        }
        return b1.multiply(b2).setScale(scale, RoundingMode.HALF_UP);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到默认小数位，以后的数字四舍五入。
     *
     * @param o1:
     * @param o2:
     * @param scale
     * @return BigDecimal
     * @Author chenyou
     * @Date 2023/4/22 14:56
     */
    public static BigDecimal div(Object o1,Object o2,Integer scale)
    {
        BigDecimal b1 = convert(o1);
        BigDecimal b2 = convert(o2);
        return div(b1, b2, Objects.isNull(scale)?DEF_DIV_SCALE:scale);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到默认小数位，以后的数字四舍五入。
     * @param b1 被除数
     * @param b2 除数
     * @return 两个参数的商
     */
    public static BigDecimal div(BigDecimal b1, BigDecimal b2)
    {
        b1 = defaultZero(b1);
        b2 = defaultZero(b2);
        return div(b1, b2, DEF_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     * @param b1 被除数
     * @param b2 除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal div(BigDecimal b1, BigDecimal b2, int scale)
    {
        if (scale < 0) {
            throw new IllegalArgumentException(EXCEPTION_MSG);
        }
        if (b1.compareTo(BigDecimal.ZERO) == 0)
        {
            return BigDecimal.ZERO;
        }
        return b1.divide(b2, scale, RoundingMode.HALF_UP);
    }

    /**
     * 提供（相对）精确的平均值运算,当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     *
     * @param i1: 被除数
     * @param i2: 除数
     * @param scale: 表示表示需要精确到小数点以后几位。
     * @return BigDecimal
     * @Author chenyou
     * @Date 2023/4/20 16:54
     */
    public static Integer avg(Integer i1,Integer i2,int scale)
    {
        BigDecimal b1 = defaultZero(i1);
        BigDecimal b2 = defaultZero(i2);
        return avg(b1,b2,scale).intValue();
    }

    /**
     * 提供（相对）精确的平均值运算,当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     *
     * @param b1: 被除数
     * @param b2: 除数
     * @param scale: 表示表示需要精确到小数点以后几位。
     * @return BigDecimal
     * @Author chenyou
     * @Date 2023/4/20 16:52
     */
    public static BigDecimal avg(BigDecimal b1,BigDecimal b2,int scale)
    {
        if (scale < 0){
            throw new IllegalArgumentException(EXCEPTION_MSG);
        }
        if (b1.compareTo(BigDecimal.ZERO) == 0){
            return BigDecimal.ZERO;
        }
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 提供精确的小数位四舍五入处理。
     * @param v 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale)
    {
        if (scale < 0)
        {
            throw new IllegalArgumentException(EXCEPTION_MSG);
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = BigDecimal.ONE;
        return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 如果num为Null，默认为0
     * @param num 数值
     * @return BigDecimal
     */
    public static BigDecimal defaultZero(BigDecimal num) {
        if (num == null) {
            return BigDecimal.ZERO;
        }
        return num;
    }

    /**
     * 如果num为Null，默认为0
     * @param num 数值
     * @return BigDecimal
     */
    public static BigDecimal defaultZero(Integer num) {
        if (num == null) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(num);
    }

    /**
     * 如果num为Null，默认为0
     * @param num 数值
     * @return BigDecimal
     */
    public static BigDecimal defaultZero(Long num) {
        if (num == null) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(num);
    }

    /**
     * 如果num为Null，默认为0
     * @param num 数值
     * @return BigDecimal
     */
    public static BigDecimal defaultZero(Double num) {
        if (num == null) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(Double.toString(num));
    }

    /**
     * 默认值
     * @param num 数值
     * @param value 默认值
     * @return BigDecimal
     */
    public static BigDecimal defaultValue(BigDecimal num, BigDecimal value) {
        if (num == null) {
            return value;
        }
        return num;
    }

    /**
     * 判断num是否为NULL或者为0
     * @param num 数值
     * @return boolean
     */
    public static boolean isNullOrZero(BigDecimal num) {
        if (num == null || num.compareTo(BigDecimal.ZERO) == 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断num是不为NULL且不为0
     * @param num 数值
     * @return boolean
     */
    public static boolean isNotNullOrZero(BigDecimal num) {
        return !isNullOrZero(num);
    }

    /**
     * 累加
     * @param num 数值
     * @return BigDecimal
     */
    public static BigDecimal add(BigDecimal... num) {
        BigDecimal total = BigDecimal.ZERO;
        for (BigDecimal n : num) {
            total = total.add(defaultZero(n));
        }
        return total;
    }

    /**
     * 累加
     * @param num 数值
     * @return BigDecimal
     */
    public static BigDecimal add(List<BigDecimal> num) {
        BigDecimal total = BigDecimal.ZERO;
        if(CollectionUtils.isEmpty(num)){
            return total;
        }
        for (BigDecimal n : num) {
            total = total.add(defaultZero(n));
        }
        return total;
    }

    /**
     * 百分比计算
     * @param baseAmount 运输金额
     * @param percent 百分比数
     * @return BigDecimal 默认四舍五入
     */
    public static BigDecimal multiplyPercent(BigDecimal baseAmount, BigDecimal percent) {
        if (defaultZero(baseAmount).compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        if (defaultZero(percent).compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        Objects.requireNonNull(baseAmount);
        return baseAmount.multiply(defaultZero(percent))
                .divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 求最小数值
     * @param num
     * @return BigDecimal
     */
    public static BigDecimal min(BigDecimal... num) {
        if (num == null) {
            return null;
        }
        Optional<BigDecimal> optional = Arrays.stream(num).min(BigDecimal::compareTo);
        if(!optional.isPresent()){
            return BigDecimal.ZERO;
        }
        return optional.get();
    }

    /**
     * 求最大数值
     * @param num
     * @return BigDecimal
     */
    public static BigDecimal max(BigDecimal... num) {
        if (num == null) {
            return null;
        }
        Optional<BigDecimal> optional = Arrays.stream(num).max(BigDecimal::compareTo);
        if(!optional.isPresent()){
            return BigDecimal.ZERO;
        }
        return optional.get();
    }

    /**
     * 转换 Integer,Long,Double等类型为BigDecimal
     *
     * @param o1:
     * @return BigDecimal
     * @Author chenyou
     * @Date 2023/4/21 17:55
     */
    public static BigDecimal convert(Object o1){
        if(o1 == null){
            return BigDecimal.ZERO;
        }
        if(o1 instanceof Integer){
            return defaultZero((Integer) o1);
        }else if(o1 instanceof Long){
            return defaultZero((Long) o1);
        }else if(o1 instanceof Double){
            return defaultZero((Double) o1);
        }else if(o1 instanceof BigDecimal){
            return (BigDecimal) o1;
        }else{
            throw new BaseException("类型转换异常，请传入指定范围类型!");
        }
    }





    public static void main(String[] args) {
        Long l = 100000000L;
        log.info(defaultZero(l).toString());
        BigDecimal a = new BigDecimal("100");
        BigDecimal b = new BigDecimal("21");

        BigDecimal add = add(a, b);
        log.info("相加:"+add);
        BigDecimal sub = sub(a, b);
        log.info("相减:"+sub);

        BigDecimal mul = mul(a, b);
        log.info("相乘:"+mul);
        BigDecimal div = div(a, b);
        log.info("相除:"+div);
        BigDecimal x = new BigDecimal("100.334");
        BigDecimal y = new BigDecimal("2.7");
        log.info(BigDecimalUtils.mul(x,y).toString());
    }

}